import ReconnectingWebSocket from 'reconnecting-websocket'
import type { Plugin } from '@vue/runtime-core'
import { inject } from 'vue'
import mitt from 'mitt'
import type { Handler } from 'mitt'

import type { ISocketMessage } from '@/common/types'

enum SendMessageType {
  INIT = 'init'
}

const emitter = mitt<ISocketMessage>()

const createSocket = () => {
  const socket = new ReconnectingWebSocket(
    'wss://cswsbro.flywaycdn.net:3030/626d68642ee9b-1699270878-14bad72237b2032f2977419e709fc1de',
    [],
    {
      debug: true
    }
  )

  const sendMessage = <T>(cmd: SendMessageType, data: T) => {
    const message = JSON.stringify({
      cmd,
      data
    })
    socket.send(message)
  }

  const sendInitial = (uid: string) => {
    sendMessage(SendMessageType.INIT, {
      uid
    })
  }

  socket.addEventListener('message', (res: MessageEvent<string>) => {
    const { cmd, data } = JSON.parse(res.data)
    emitter.emit(cmd, data)
  })

  const onMessage = <Type extends keyof ISocketMessage>(
    type: Type,
    handler: Handler<ISocketMessage[Type]>
  ) => {
    emitter.on(type, handler)
  }

  return {
    sendInitial,
    onMessage
  }
}

const socketSymbol = Symbol('socket')

type CreateSocketResult = ReturnType<typeof createSocket>

export const Socket: Plugin & { useSocket: () => CreateSocketResult } = {
  install(app) {
    const socket = createSocket()
    app.provide(socketSymbol, socket)
  },
  useSocket() {
    return inject(socketSymbol) as CreateSocketResult
  }
}
